package cn.hg.solon.youcan.system.provider;

import cn.hg.solon.youcan.common.constant.AppConstants;
import cn.hg.solon.youcan.common.constant.WebConstants;
import cn.hg.solon.youcan.common.enums.BeanStatus;
import cn.hg.solon.youcan.common.enums.DataScope;
import cn.hg.solon.youcan.common.exception.ServiceException;
import cn.hg.solon.youcan.flex.util.QueryWrapperUtil;
import cn.hg.solon.youcan.system.entity.Dept;
import cn.hg.solon.youcan.system.entity.Role;
import cn.hg.solon.youcan.system.entity.SysDept;
import cn.hg.solon.youcan.system.mapper.SysDeptMapper;
import cn.hg.solon.youcan.system.mapper.SysRoleDeptMappingMapper;
import cn.hg.solon.youcan.system.mapper.SysUserMapper;
import cn.hg.solon.youcan.system.service.DeptService;
import com.mybatisflex.core.query.*;
import com.mybatisflex.solon.service.impl.ServiceImpl;
import org.dromara.hutool.core.bean.BeanUtil;
import org.dromara.hutool.core.text.StrValidator;
import org.dromara.hutool.core.tree.MapTree;
import org.dromara.hutool.core.tree.TreeNodeConfig;
import org.dromara.hutool.core.tree.TreeUtil;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.db.sql.Order;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import org.noear.solon.data.annotation.Tran;

import java.util.List;
import java.util.Map;

import static cn.hg.solon.youcan.system.entity.table.SysDeptTableDef.SYS_DEPT;
import static cn.hg.solon.youcan.system.entity.table.SysRoleDeptMappingTableDef.SYS_ROLE_DEPT_MAPPING;
import static cn.hg.solon.youcan.system.entity.table.SysRoleTableDef.SYS_ROLE;
import static cn.hg.solon.youcan.system.entity.table.SysUserTableDef.SYS_USER;

/**
 * @author 胡高
 */
@Component
public class SysDeptProvider extends ServiceImpl<SysDeptMapper, SysDept> implements DeptService {

    @Inject
    private SysRoleDeptMappingMapper roleDeptMappingMapper;

    @Inject
    private SysUserMapper userMapper;

    /**
     * <pre>
     *  构建QueryWrapper
     *  SELECT *
     *  FROM `sys_dept`
     *  WHERE (`name` LIKE ?
     *          OR `leader` LIKE ?
     *          OR `phone` LIKE ?
     *          OR `email` LIKE ?
     *          OR FIND_IN_SET(`id`, (
     *              SELECT GROUP_CONCAT( `ancestors` )
     *                  FROM `sys_dept`
     *                  WHERE `name` LIKE ? OR `leader` LIKE ? OR `phone` LIKE ? OR `email` LIKE ?
     *              )
     *          )
     *      )
     *      AND `del` = ?
     *  </pre>
     */
    private QueryWrapper buildQueryWrapper(Integer parentId, Map<?, ?> paraMap) {
        String word = (String) paraMap.get("word");

        QueryCondition findInSet = new FindInSetQueryCondition("OR", SYS_DEPT.ID, QueryWrapper.create()
                .select(new FunctionQueryColumn("GROUP_CONCAT", SYS_DEPT.ANCESTORS))
                .from(SYS_DEPT)
                .where(SYS_DEPT.NAME.like(word).when(StrValidator.isNotEmpty(word))
                        .or(SYS_DEPT.LEADER.like(word).when(StrValidator.isNotEmpty(word)))
                        .or(SYS_DEPT.PHONE.like(word).when(StrValidator.isNotEmpty(word)))
                        .or(SYS_DEPT.EMAIL.like(word).when(StrValidator.isNotEmpty(word)))
                )
        ).when(StrValidator.isNotEmpty(word));

        QueryWrapper query = QueryWrapper.create()
                .from(SYS_DEPT)
                .where(SYS_DEPT.NAME.like(word).when(StrValidator.isNotEmpty(word))
                        .or(SYS_DEPT.LEADER.like(word).when(StrValidator.isNotEmpty(word)))
                        .or(SYS_DEPT.PHONE.like(word).when(StrValidator.isNotEmpty(word)))
                        .or(SYS_DEPT.EMAIL.like(word).when(StrValidator.isNotEmpty(word)))
                        .or(findInSet)
                        .when(StrValidator.isNotEmpty(word))
                );

        // ORDER BY `sort`
        return QueryWrapperUtil.applyOrderBy(query, new Order(SYS_DEPT.SORT.getName()));
    }

    @Override
    public boolean checkUnique(Dept bean) {
        // 查找已存在记录
        QueryWrapper query = QueryWrapper.create()
                .where(SYS_DEPT.PARENT_ID.eq(bean.getParentId())
                        .and(SYS_DEPT.NAME.eq(bean.getName()))
                        .and(SYS_DEPT.ID.ne(bean.getId()))
                );

        return ObjUtil.isNull(this.getOne(query));
    }

    @Tran
    @Override
    public boolean delete(Integer id) {
        if (this.getMapper().selectCountByCondition(SYS_DEPT.PARENT_ID.eq(id)) > 0) {
            throw new ServiceException("存在子项，不允许删除！");
        }

        /*
         * 检查是否已分配于角色
         */
        long count = this.roleDeptMappingMapper.selectCountByQuery(QueryWrapper.create()
                .leftJoin(SYS_ROLE).on(SYS_ROLE.ID.eq(SYS_ROLE_DEPT_MAPPING.ROLE_ID))
                .where(SYS_ROLE_DEPT_MAPPING.DEPT_ID.eq(id).and(SYS_ROLE.DATA_SCOPE.eq(DataScope.CUSTOM.name())))
        );
        if (count > 0L) {
            throw new ServiceException("已分配于角色，不允许删除！");
        }

        /*
         * 检查部门下是否存在用户
         */
        count = this.userMapper.selectCountByQuery(QueryWrapper.create().where(SYS_USER.DEPT_ID.eq(id)));
        if (count > 0L) {
            throw new ServiceException("部门下存在用户，不允许删除！");
        }

        return this.getMapper().deleteById(id) > 0;
    }

    @Override
    public SysDept get(Integer id) {
        return this.getMapper().selectOneById(id);
    }

    @Tran
    @Override
    public boolean insert(Dept bean) {
        /*
         * 无父对象ID时，默认为0
         */
        if (ObjUtil.isNull(bean.getParentId())) {
            bean.setParentId(0);
        }

        if (!this.checkUnique(bean)) {
            throw new ServiceException("部门名称已经存在，请更换其它值！");
        }

        SysDept partner = this.get(bean.getParentId());
        bean.setAncestors(ObjUtil.isNull(partner) ? "0" : partner.getAncestors() + "," + bean.getParentId());

        SysDept cloneBean = BeanUtil.copyProperties(bean, SysDept.class);

        return this.getMapper().insert(cloneBean) > 0;
    }

    @Override
    public List<SysDept> listBy(Map<String, Object> paraMap) {
        List<SysDept> list = this.getMapper().selectListByQuery(this.buildQueryWrapper(0, paraMap));

        return list;
    }

    @Override
    public List<SysDept> listByRole(Role bean) {
        /*
            SELECT d.*
            FROM sys_dept AS d
                LEFT JOIN sys_role_dept_mapping AS rd ON rd.dept_id = d.id
            WHERE d.del = FALSE AND rd.role_id = ${bean.id}
         */
        QueryWrapper query = QueryWrapper.create().select(new DistinctQueryColumn(SYS_DEPT.ALL_COLUMNS))
                .leftJoin(SYS_ROLE_DEPT_MAPPING).on(SYS_ROLE_DEPT_MAPPING.DEPT_ID.eq(SYS_DEPT.ID))
                .where(SYS_ROLE_DEPT_MAPPING.ROLE_ID.eq(bean.getId()))
                .orderBy(new QueryOrderBy(new QueryColumn(SYS_DEPT.SORT.getName()), WebConstants.ORDER_DIRECTION_ASC));

        List<SysDept> list = this.getMapper().selectListByQuery(query);

        return list;
    }

    @Override
    public List<SysDept> listByStatus(String status) {
        List<SysDept> list = this.getMapper().selectListByCondition(SYS_DEPT.STATUS.eq(status));

        return list;
    }

    @Tran
    @Override
    public boolean update(Dept bean) {
        /*
         * 无父对象ID时，默认为0
         */
        if (ObjUtil.isNull(bean.getParentId())) {
            bean.setParentId(0);
        }
        Integer newParentId = bean.getParentId();

        if (!this.checkUnique(bean)) {
            throw new ServiceException("部门名称已经存在，请更换其它值！");
        }

        SysDept parent = this.get(bean.getParentId());
        bean.setAncestors(ObjUtil.isNull(parent) ? "0" : parent.getAncestors() + "," + bean.getParentId());

        SysDept cloneBean = this.get(bean.getId());
        Integer currentParentId = cloneBean.getParentId();
        cloneBean = BeanUtil.copyProperties(bean, SysDept.class);

        boolean ret = this.getMapper().update(cloneBean) > 0;

        // 如果上级部门有变化，则更新所有下级部门的ancestors字段
        if (!ObjUtil.equals(newParentId, currentParentId)) {
            this.updateChildrenAncestors(cloneBean);
        }

        return ret;
    }

    @Override
    public List<MapTree<Integer>> treeBy(Map<String, Object> paraMap) {
        List<SysDept> list = this.listBy(paraMap);

        TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
        // 自定义属性名 都要默认值的
        treeNodeConfig.setIdKey("id");
        treeNodeConfig.setParentIdKey("parentId");
        treeNodeConfig.setNameKey("name");

        List<MapTree<Integer>> trees = TreeUtil.build(list, 0, treeNodeConfig, (treeNode, tree) -> {
            tree.setId((Integer) treeNode.getId());
            tree.setParentId((Integer) treeNode.getParentId());
            tree.setName((String) treeNode.getName());
            //扩展属性
            tree.putExtra("disabled", !ObjUtil.equals(BeanStatus.ON.name(), treeNode.getStatus()));
        });

        return trees;
    }

    private void updateChildrenAncestors(SysDept bean) {
        // 查询所有直接下级
        List<SysDept> childrenList = this.getMapper().selectListByCondition(SYS_DEPT.PARENT_ID.eq(bean.getId()));

        // 更新下级的ancestors字段
        for (SysDept item : childrenList) {
            item.setAncestors(bean.getAncestors() + "," + item.getParentId());
            this.getMapper().update(item);

            // 递归更新再下一级
            this.updateChildrenAncestors(item);
        }
    }
}

